#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/power_supply.h>
//#include <linux/video_output.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/jiffies.h>
#include <linux/miscdevice.h>
//#include <asm/bootinfo.h>
#include <linux/module.h>
#include <linux/device.h>
#include <ec_it8528.h>

#define LPC_EC_INDEX_PORT 0x2E
#define LPC_EC_DATA_PORT 0x2F

#define SIO_CONFIG_CTRL       0x02
#define SIO_CONFIG_PORT       0x2E

#define VOID       void
#define UINT8       unsigned char
#define UINT16       unsigned short

//#define linux_outb outb
//#define linux_inb inb

unsigned char linux_inb(unsigned short int port);
void linux_outb( unsigned short int port, unsigned char value);

unsigned char linux_inb(unsigned short int port)
{
  return inb(port);
}
void linux_outb( unsigned short int port, unsigned char value)
{
  outb(value,port);
}



#define BIT1 0x02

static ssize_t ec_it8733_dev_config(unsigned char *ecram_vals);
static struct class  *ec_it8733_dev_class;
static struct device *ec_it8733_dev_class_dev;


static int ec_it8733_dev_open(struct inode *inode, struct file *file)
{
	return 0;
}

#define EC_DEV_IN 1001
#define EC_DEV_OUT 1002

#define EC_DEV_BYTE_IN 1101
#define EC_DEV_BYTE_OUT 1102

typedef struct
{
  int nlen;
  unsigned short int port;
  unsigned char value;

}  __attribute__ ((packed)) typEcDev;


char strbuf[100]={0};
static long ec_it8733_dev_ioctl(struct file *file,
    unsigned int cmd, unsigned long arg)
{
  int i;
  int err = 0;
  void __user *uarg = (void __user *) arg;

  //printk("fdfsd\n");

  switch (cmd) 
  {
    case EC_DEV_IN:
    {
  #if 0  
      mutex_unlock(&rtc->ops_lock);

      err = rtc_read_alarm(rtc, &alarm);
      if (err < 0)
        return err;
  #endif
      if (copy_to_user(uarg, &strbuf, sizeof(strbuf)))
        err = -EFAULT;
      return err;
    }

    case EC_DEV_OUT:
    {
      

      if (copy_from_user(&strbuf, uarg, sizeof(strbuf)))
        return -EFAULT; 
      for( i=0;i<sizeof(strbuf);i++)
      {
        strbuf[i]=strbuf[i]+1;
      }
      return err;             
    }

    case EC_DEV_BYTE_IN:
    {
      typEcDev ecdev;

      if (copy_from_user(&ecdev, uarg, sizeof(ecdev)))
        return -EFAULT; 
      ecdev.value=inb(ecdev.port);
      if (copy_to_user(uarg, &ecdev, sizeof(ecdev)))
        return -EFAULT; 
      return 0;    
    }

    case EC_DEV_BYTE_OUT:
    {
      typEcDev ecdev;

      if (copy_from_user(&ecdev, uarg, sizeof(ecdev)))
        return -EFAULT; 


      outb(ecdev.value,ecdev.port);
      return 0;   
    }




  #if 0
  struct rtc_device *rtc = file->private_data;
  const struct rtc_class_ops *ops = rtc->ops;
  struct rtc_time tm;
  struct rtc_wkalrm alarm;
  void __user *uarg = (void __user *) arg;

  err = mutex_lock_interruptible(&rtc->ops_lock);
  if (err)
    return err;

  /* check that the calling task has appropriate permissions
   * for certain ioctls. doing this check here is useful
   * to avoid duplicate code in each driver.
   */
  switch (cmd) {
  case RTC_EPOCH_SET:
  case RTC_SET_TIME:
    if (!capable(CAP_SYS_TIME))
      err = -EACCES;
    break;

  case RTC_IRQP_SET:
    if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
      err = -EACCES;
    break;

  case RTC_PIE_ON:
    if (rtc->irq_freq > rtc->max_user_freq &&
        !capable(CAP_SYS_RESOURCE))
      err = -EACCES;
    break;
  }

  if (err)
    goto done;

  /*
   * Drivers *SHOULD NOT* provide ioctl implementations
   * for these requests.  Instead, provide methods to
   * support the following code, so that the RTC's main
   * features are accessible without using ioctls.
   *
   * RTC and alarm times will be in UTC, by preference,
   * but dual-booting with MS-Windows implies RTCs must
   * use the local wall clock time.
   */

  switch (cmd) {
  case RTC_ALM_READ:
    mutex_unlock(&rtc->ops_lock);

    err = rtc_read_alarm(rtc, &alarm);
    if (err < 0)
      return err;

    if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
      err = -EFAULT;
    return err;

  case RTC_ALM_SET:
    mutex_unlock(&rtc->ops_lock);

    if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
      return -EFAULT;

    alarm.enabled = 0;
    alarm.pending = 0;
    alarm.time.tm_wday = -1;
    alarm.time.tm_yday = -1;
    alarm.time.tm_isdst = -1;

    /* RTC_ALM_SET alarms may be up to 24 hours in the future.
     * Rather than expecting every RTC to implement "don't care"
     * for day/month/year fields, just force the alarm to have
     * the right values for those fields.
     *
     * RTC_WKALM_SET should be used instead.  Not only does it
     * eliminate the need for a separate RTC_AIE_ON call, it
     * doesn't have the "alarm 23:59:59 in the future" race.
     *
     * NOTE:  some legacy code may have used invalid fields as
     * wildcards, exposing hardware "periodic alarm" capabilities.
     * Not supported here.
     */
    {
      unsigned long now, then;

      err = rtc_read_time(rtc, &tm);
      if (err < 0)
        return err;
      rtc_tm_to_time(&tm, &now);

      alarm.time.tm_mday = tm.tm_mday;
      alarm.time.tm_mon = tm.tm_mon;
      alarm.time.tm_year = tm.tm_year;
      err  = rtc_valid_tm(&alarm.time);
      if (err < 0)
        return err;
      rtc_tm_to_time(&alarm.time, &then);

      /* alarm may need to wrap into tomorrow */
      if (then < now) {
        rtc_time_to_tm(now + 24 * 60 * 60, &tm);
        alarm.time.tm_mday = tm.tm_mday;
        alarm.time.tm_mon = tm.tm_mon;
        alarm.time.tm_year = tm.tm_year;
      }
    }

    return rtc_set_alarm(rtc, &alarm);

  case RTC_RD_TIME:
    mutex_unlock(&rtc->ops_lock);

    err = rtc_read_time(rtc, &tm);
    if (err < 0)
      return err;

    if (copy_to_user(uarg, &tm, sizeof(tm)))
      err = -EFAULT;
    return err;

  case RTC_SET_TIME:
    mutex_unlock(&rtc->ops_lock);

    if (copy_from_user(&tm, uarg, sizeof(tm)))
      return -EFAULT;

    return rtc_set_time(rtc, &tm);

  case RTC_PIE_ON:
    err = rtc_irq_set_state(rtc, NULL, 1);
    break;

  case RTC_PIE_OFF:
    err = rtc_irq_set_state(rtc, NULL, 0);
    break;

  case RTC_AIE_ON:
    mutex_unlock(&rtc->ops_lock);
    return rtc_alarm_irq_enable(rtc, 1);

  case RTC_AIE_OFF:
    mutex_unlock(&rtc->ops_lock);
    return rtc_alarm_irq_enable(rtc, 0);

  case RTC_UIE_ON:
    mutex_unlock(&rtc->ops_lock);
    return rtc_update_irq_enable(rtc, 1);

  case RTC_UIE_OFF:
    mutex_unlock(&rtc->ops_lock);
    return rtc_update_irq_enable(rtc, 0);

  case RTC_IRQP_SET:
    err = rtc_irq_set_freq(rtc, NULL, arg);
    break;

  case RTC_IRQP_READ:
    err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
    break;

  case RTC_WKALM_SET:
    mutex_unlock(&rtc->ops_lock);
    if (copy_from_user(&alarm, uarg, sizeof(alarm)))
      return -EFAULT;

    return rtc_set_alarm(rtc, &alarm);

  case RTC_WKALM_RD:
    mutex_unlock(&rtc->ops_lock);
    err = rtc_read_alarm(rtc, &alarm);
    if (err < 0)
      return err;

    if (copy_to_user(uarg, &alarm, sizeof(alarm)))
      err = -EFAULT;
    return err;

  default:
    /* Finally try the driver's ioctl interface */
    if (ops->ioctl) {
      err = ops->ioctl(rtc->dev.parent, cmd, arg);
      if (err == -ENOIOCTLCMD)
        err = -ENOTTY;
    } else
      err = -ENOTTY;
    break;
  }


done:
  mutex_unlock(&rtc->ops_lock);
  #endif
  }
  return err;
}



ssize_t ec_it8733_dev_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	unsigned char ecram_vals[256];
	//unsigned int i;
#if 0
	for(i = 0; i < 256;i++)
	{
		ecram_vals[i] = it8528_read(i);
	}
#endif
	memset(ecram_vals, 0xFF, sizeof(ecram_vals));
	ec_it8733_dev_config(&ecram_vals[0]);

  if(copy_to_user(buf, ecram_vals, sizeof(ecram_vals)))
    return -EFAULT; 

	return sizeof(ecram_vals);
}

static ssize_t ec_it8733_dev_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val;	

	if(copy_from_user(&val, buf, count))
    return -EFAULT; 


	it8528_write((val>>8)&0xff , val&0xff);
	
	return 0;
}


static VOID
EnterConfigMode (
VOID
  )
/*++

Routine Description:
  Enter Super IO Config Mode.

Arguments:

Returns:
  None

--*/  
{
  UINT8  PnpKey;

  PnpKey = 0x87;
  linux_outb(SIO_CONFIG_PORT, PnpKey);
  PnpKey = 0x01;
  linux_outb(SIO_CONFIG_PORT, PnpKey);
  PnpKey = 0x55;
  linux_outb(SIO_CONFIG_PORT, PnpKey);
  PnpKey = 0x55;
  linux_outb(SIO_CONFIG_PORT, PnpKey);
  
  return;
}

static VOID
ExitConfigMode (
  VOID
  )
/*++

Routine Description:
  Exit Super IO Config Mode.

Arguments:

Returns:
  None

--*/    
{
  UINT8  RegData = 0x00;

  linux_outb (LPC_EC_INDEX_PORT, SIO_CONFIG_CTRL);
  RegData = linux_inb (LPC_EC_DATA_PORT) | BIT1;
  linux_outb (LPC_EC_INDEX_PORT, SIO_CONFIG_CTRL);  
  linux_outb (LPC_EC_DATA_PORT, RegData);
  
  return;
}

static ssize_t ec_it8733_dev_config(unsigned char *ecram_vals)
{
    int i;
	unsigned char Chipid0,Chipid1 =0;
	UINT16  DataTemp;
  EnterConfigMode();
  EnterConfigMode();
  linux_outb (LPC_EC_INDEX_PORT, 0x20);  
  Chipid0 = linux_inb (LPC_EC_DATA_PORT);
  ecram_vals[0]=Chipid0;

  linux_outb (LPC_EC_INDEX_PORT, 0x21);
  Chipid1 = linux_inb (LPC_EC_DATA_PORT); 
  ecram_vals[1]=Chipid1;

  linux_outb (LPC_EC_INDEX_PORT, 0x22);
  ecram_vals[2] = linux_inb (LPC_EC_DATA_PORT);

#if  0
  linux_outb (LPC_EC_INDEX_PORT, 0x22);
  linux_outb (LPC_EC_DATA_PORT,0xF5);
  linux_outb (LPC_EC_INDEX_PORT, 0x22);
  ecram_vals[3] = linux_inb (LPC_EC_DATA_PORT);
#endif

  for(i=0;i<15;i++)
  {
    linux_outb (LPC_EC_INDEX_PORT, 0x07);
    linux_outb (LPC_EC_DATA_PORT, 0x00);

    linux_outb (LPC_EC_INDEX_PORT, i);

      ecram_vals[0x20+i] = linux_inb (LPC_EC_DATA_PORT);

  }





  //DEBUG((EFI_D_ERROR, "Lyang: In Ls7a.c, line %d. Chipid0 %x Chipid1 %x\n", __LINE__,Chipid0,Chipid1));
  linux_outb (LPC_EC_INDEX_PORT, 0x7);  
  linux_outb (LPC_EC_DATA_PORT, 0x7);  
  linux_outb (LPC_EC_INDEX_PORT, 0x62); 
  linux_outb (LPC_EC_DATA_PORT, 0x04);  
  linux_outb (LPC_EC_INDEX_PORT, 0x63); 
  linux_outb (LPC_EC_DATA_PORT, 0x00); 
  linux_outb (LPC_EC_INDEX_PORT, 0x27);  
  DataTemp = linux_inb (LPC_EC_DATA_PORT);
  ecram_vals[0x10]=DataTemp;
  //DEBUG((EFI_D_ERROR, "Lyang: In EncryDriverPei.c, line %d.Data0 0x%x \n", __LINE__,Data0));
  linux_outb (LPC_EC_INDEX_PORT, 0x27);  
  linux_outb (LPC_EC_DATA_PORT, DataTemp|0x38); 
  linux_outb (LPC_EC_INDEX_PORT, 0xC2); 
  DataTemp = linux_inb (LPC_EC_DATA_PORT);
  ecram_vals[0x11]=DataTemp;
  //DEBUG((EFI_D_ERROR, "Lyang: In EncryDriverPei.c, line %d.Data0 0x%x \n", __LINE__,Data0));
  linux_outb (LPC_EC_INDEX_PORT, 0xC2);  
  linux_outb (LPC_EC_DATA_PORT, DataTemp|0x08);  
  linux_outb (LPC_EC_INDEX_PORT, 0xCA); 
  DataTemp = linux_inb (LPC_EC_DATA_PORT);
  ecram_vals[0x12]=DataTemp;
  //DEBUG((EFI_D_ERROR, "Lyang: In EncryDriverPei.c, line %d.Data1 0x%x \n", __LINE__,Data1));
//Output settings
  linux_outb (LPC_EC_INDEX_PORT, 0xCA);  
  linux_outb (LPC_EC_DATA_PORT, DataTemp|0x08);  
  DataTemp = linux_inb(0x402);
  ecram_vals[0x13]=DataTemp;
  printk("%02X \n",DataTemp);
  linux_outb (0x402, (DataTemp | 0x08));
  ExitConfigMode();	
  return 0;
}

static struct file_operations ec_it8733_dev_fops = {
    .owner  =   THIS_MODULE,    /* ����һ���꣬�������ģ��ʱ�Զ�������__this_module���� */
    .open   =   ec_it8733_dev_open,     
	.read	=	ec_it8733_dev_read,	  
	.write	=	ec_it8733_dev_write,
  //.ioctl  = ec_it8733_dev_ioctl,	
  .unlocked_ioctl  =ec_it8733_dev_ioctl,
};


int major;
static int ec_it8733_dev_init(void)
{
	major = register_chrdev(0, "ec_it8733_dev", &ec_it8733_dev_fops); // ע��, �����ں�

	ec_it8733_dev_class = class_create(THIS_MODULE, "ecit8733dev");

	ec_it8733_dev_class_dev = device_create(ec_it8733_dev_class, NULL, MKDEV(major, 0), NULL, "ec_it8733"); /* /dev/ecit8733*/


	return 0;
}

static void ec_it8733_dev_exit(void)
{
	unregister_chrdev(major, "ec_it8733_dev");
	device_unregister(ec_it8733_dev_class_dev);
	class_destroy(ec_it8733_dev_class);
}


module_init(ec_it8733_dev_init);

module_exit(ec_it8733_dev_exit);

MODULE_LICENSE("GPL");

